using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.IO;
using System.Text;
using Oracle.DataAccess.Client;

namespace LinqGen
{
	/// <summary>
	/// 
	/// </summary>
	public class BatchGenerator
	{
		#region Constants

		private static readonly int TABLE_OWNER_INDEX = 0;
		private static readonly int TABLE_NAME_INDEX = 1;
		private static readonly int COLUMN_NAME_INDEX = 2;
		private static readonly int INTERNAL_COLUMN_ID_INDEX = 3;
		private static readonly int DATA_TYPE_INDEX = 4;
		private static readonly int DATA_SIZE_INDEX = 5;
		private static readonly int DATA_SCALE_INDEX = 6;
		private static readonly int NULLABLE_INDEX = 7;
		private static readonly int CONSTRAINT_NAME_INDEX = 8;
		private static readonly int REFERENCE_TABLE_INDEX = 9;
		private static readonly int CONSTRAINT_TYPE_INDEX = 10;
		private static readonly int SEQUENCE_NAME_INDEX = 11;

		#endregion

		#region Fields

		private string _prefix;
		private string _directory;

		Dictionary<string, string> _map;

		#endregion

		#region Constructors

		/// <summary>
		/// 
		/// </summary>
		public BatchGenerator()
		{
			InitializeConfiguration();
			InitializeDomains();
		}

		#endregion

		#region Generation

		/// <summary>
		/// 
		/// </summary>
		public void Execute()
		{
			string comparer = string.Empty;
			// Retrieve all candidates.
			IDataReader reader = DataTool.GetAllTables();
			//
			CodeGeneratorClass generator = null;

			string pathName = string.Empty;

			// Generate each class
			while (reader.Read())
			{
				string tableName = reader.GetString(TABLE_NAME_INDEX);

				if (!tableName.Equals(comparer, StringComparison.OrdinalIgnoreCase))
				{
					comparer = tableName;

					if (generator != null)
					{
						generator.CsPathName = pathName;
						if (!Directory.Exists(generator.CsPathName))
						{
							Directory.CreateDirectory(generator.CsPathName);
						}
						generator.Generate(false);
					}
					generator = GenerateClass(reader);
					pathName = _directory;
				}
				if (null != generator)
				{
					GenerateColumn(reader, generator);
				}

			}
		}

		#endregion

		#region Private Implementation

		/// <summary>
		/// 
		/// </summary>
		/// <param name="row"></param>
		private CodeGeneratorClass GenerateClass(IDataReader row)
		{
			string tableName = row.GetString(TABLE_NAME_INDEX);
			string prefix = ConfigurationManager.AppSettings["TablePrefix"] as string;
			string className = NameDeductor.MapTableNameToClass(tableName);
			string directory = string.Empty;
			CodeGeneratorClass generator = new CodeGeneratorClass(_prefix, directory, className, tableName);
			generator.Domains = _map;
			return generator;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="row"></param>
		private void GenerateColumn(IDataReader row, CodeGeneratorClass generator)
		{
			string tableName = row.GetString(TABLE_NAME_INDEX);
			string columnName = row.GetString(COLUMN_NAME_INDEX);

			if (!columnName.StartsWith("AUDIT_"))
			{
				string columnType = row.GetString(DATA_TYPE_INDEX);

				string mappingName = NameDeductor.MapColumnNameToField(columnName);

				bool isPrimaryKey = (row[CONSTRAINT_TYPE_INDEX] != DBNull.Value && row.GetString(CONSTRAINT_TYPE_INDEX) == "P");
				bool isForeignKey = (row[CONSTRAINT_TYPE_INDEX] != DBNull.Value && row.GetString(CONSTRAINT_TYPE_INDEX) == "R");

				string datatype = row.GetString(DATA_TYPE_INDEX);

				bool isNullable = (string.Compare(row.GetString(NULLABLE_INDEX), "Y", true) == 0);
				int size = (row[DATA_SIZE_INDEX] == DBNull.Value) ? 0 : Convert.ToInt32(row[DATA_SIZE_INDEX]);
				int precision = (row[DATA_SCALE_INDEX] == DBNull.Value) ? 0 : Convert.ToInt32(row[DATA_SCALE_INDEX]);

				bool hasDecimalPlaces = (precision != 0);

				Wrapper oracleDotnetWrapper = new Wrapper();
				Type mappingType = oracleDotnetWrapper.GetDotNetType(datatype, isPrimaryKey, isForeignKey, hasDecimalPlaces);

				string sequenceName = (row[SEQUENCE_NAME_INDEX] == DBNull.Value) ? string.Empty : row.GetString(SEQUENCE_NAME_INDEX);

				generator.AddMappedColumn(columnName, columnType, mappingName, mappingType, isPrimaryKey, isForeignKey, isNullable, size, precision, sequenceName);

				if (isForeignKey)
				{
					GenerateForeignKey(row, generator);
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="row"></param>
		/// <param name="generator"></param>
		private void GenerateForeignKey(IDataReader row, CodeGeneratorClass generator)
		{
			string columnType = row.GetString(DATA_TYPE_INDEX);
			string columnName = NameDeductor.MapColumnNameToField(row.GetString(COLUMN_NAME_INDEX));

			string reference_table = row.GetString(REFERENCE_TABLE_INDEX);

			string constraintName = row.GetString(CONSTRAINT_NAME_INDEX);
			string className = NameDeductor.ExtractEntityNameFromTableName(row.GetString(REFERENCE_TABLE_INDEX));
			string otherClass = NameDeductor.MapTableNameToClass(row.GetString(REFERENCE_TABLE_INDEX));
			bool isNullable = (string.Compare(row.GetString(NULLABLE_INDEX), "Y", true) == 0);
			string mappingName = NameDeductor.ExtractEntityNameFromTableName(row.GetString(REFERENCE_TABLE_INDEX));

			string procedureName = string.Empty;

			generator.AddMappedColumnFk(className, otherClass, columnName, constraintName, mappingName, otherClass, isNullable, reference_table, procedureName);
		}

		/// <summary>
		/// 
		/// </summary>
		private void InitializeConfiguration()
		{
			this._prefix = ConfigurationManager.AppSettings["NameSpacePrefixName"];
			this._directory = ConfigurationManager.AppSettings["GeneratedClassPath"];
		}

		private void InitializeDomains()
		{
			this._map = new Dictionary<string, string>();
		}

		#endregion
	}
}
